import utils from '@/utils/index.js'

const { err, msg, req, router } = utils
import Common from '../common/common.js'

import global from '@/global.js'

// 本地字段与数据库字段的对应关系
const schema = {
  // 用户id
  uid: { default: null, dbKey: '_id' },
  // 账户
  account: { default: '' },
  // 昵称
  nickname: { default: '' },
  // 简介信息
  motto: { default: '' },
  // 头像
  avatar: { default: '' },
  // 手机号
  mobile: { default: '' },
  // 八卦APP使用情况
  baguaAppData: { default: {} },
  // 微信平台身份id
  wxOpenid: { default: {}, dbKey: 'wx_openid' },
}

class User extends Common {
  // User是一个单例类
  constructor () {
    super()
    
    if (typeof User.instance === 'object') {
      return User.instance
    }
    User.instance = this
    // 根据平台的不同进行设置
    User.platformSet()
    
    this.initBySchema(schema)
    
    return this
  }
  
  // 登出
  logout () {
    this.initBySchema(schema)
  }
  
  // 根据平台的不同进行设置
  static platformSet () {
    // User.platformOptions = {}
    // #ifdef MP-WEIXIN
    User.platformOptions = {
      // JSAPI 提供商
      jsApiProvider: 'wxPay',
      // 通常提供商
      commonProvider: 'wx',
      // 交易类型
      tradeType: 'JSAPI',
      // 应用类型：起局APP小程序，便于后台获取APPID
      appType: 'baguaWxMp',
      // openid的键值
      openidKeys: ['wxOpenid', 'mp-weixin']
    }
    // #endif
  }
  
  // 自动登录
  async autoLogin () {
    const res = await this.userCloudAuth('user-center', 'checkToken')
    // 登录成功，记录用户信息
    this.setData(res.userInfo)
  }
  
  // 小程序平台登录
  async mpLogin () {
    // 确认登录平台
    const platform = User.platformOptions.commonProvider
    
    // 根据平台选择对应的登录方式
    const actionSet = {
      wx: 'loginByWeixin'
    }
    
    // 小程序通过uni-app的方法实现登录
    const code = await User.getCode(platform)
    const loginAction = actionSet[platform] ? actionSet[platform] : actionSet.wx
    const codeRes = await User.userCloud('user-center', loginAction, { code })
    
    // 登录没有问题，保存自动登录凭证
    User.setToken(codeRes.token, codeRes.tokenExpired)
    
    // 用token换取用户信息
    try {
      let userRes = await User.userCloud('user-center', 'checkToken')
      
      // 如果连用户名都没有，说明没有获取用户信息，则进行获取
      if (!userRes.userInfo.nickname) {
        const platUserRes = await User.getUserInfo(platform)
        console.log('从平台方获取到的用户信息', platUserRes)
        
        // 设置用户名
        const setRes = await User.userCloud('user-center', 'updateUser', {
          avatar: platUserRes.userInfo.avatarUrl,
          nickname: platUserRes.userInfo.nickName,
        })
        // 后端成功设置后，前端也跟进
        userRes.userInfo.avatar = platUserRes.userInfo.avatarUrl
        userRes.userInfo.nickname = platUserRes.userInfo.nickName
      }
      // 读取用户数据
      this.setData(userRes.userInfo)
      return userRes
    }
    catch (e) {
      // 出现错误，也代表登录失败，同样要删除自动登录凭证
      User.removeToken()
      return Promise.reject(e)
    }
  }
  
  // 获取软件，onTrial是否为试用版
  async getBaguaApp (description, price, duration, onTrial = false) {
    const res = await this.userCloudAuth('pay', 'getBaguaApp', {
      payOptions: this.getPayOptions(),
      description,
      price,
      duration,
      onTrial
    })
    if (res.orderInfo) {
      // 不为0元时，进行微信支付
      await User.pay(res.orderInfo)
    }
    // 支付成功或0元购买后，先通知后台进行更新
    try {
      await User.userCloud('user-center', 'updateUser', {
        baguaAppData: res.baguaAppData
      })
      // 更新本地购买数据
      this.baguaAppData = res.baguaAppData
      return '购买成功'
    }
    catch (e) {
      return '支付成功，但用户数据更新出现问题，如不能使用请联系客服'
    }
  }
  
  // 修改用户信息
  async update (data = {}) {
    const dbData = this.toDbData(data)
    await this.userCloudAuth('user-center', 'updateUser', dbData)
    this.setData(dbData)
  }
  
  // 绑定手机号
  async bindMobile (data = {}) {
    await this.userCloudAuth('user-center', 'bindMobile', data)
    if (data.password && data.password !== '') {
      // 首次绑定手机，还需要重置密码
      await this.userCloudAuth('user-center', 'resetPwd', {
        password: data.password
      })      
    }
    // 重置成功后，更改用户信息
    this.mobile = data.mobile
  }
  
  // 检查用户是否绑定手机
  checkMobile () {
    if (this.mobile === '' || !this.mobile) {
      msg.modal({
        title: '绑定手机',
        content: '绑定手机可以在多平台统一账户信息，是否前往'
      })
      .then((res) => {
        if (res) {
          router.push('bindMobile')
        }
      })
    }
  }
  
  // 用户登录引导
  loginCheck (txt = '必须登录后才可进行操作，是否登录') {
    if (this.uid === null) {
      msg.modal({
        title: '请登录',
        content: txt
      })
      .then((res) => {
        if (res) {
          router.push('login')
        }
      })
      return false
    }
    return true
  }
  
  // 获取支付参数
  getPayOptions () {
    const openidKeys = User.platformOptions.openidKeys
    return {
      provider: User.platformOptions.commonProvider,
      openid: this[openidKeys[0]][openidKeys[1]],
      appType: User.platformOptions.appType,
      tradeType: User.platformOptions.tradeType
    }
  }
  
  // 进阶功能检查
  baguaAppCheck () {
    // 测试时直接返回true，避免验证，正式版本注释掉
    // return true
    
    // 代码审核期间，跳过付费验证
    if (global.productEnv.hidePay) {
      return true
    }
    
    let sign = true
    sign = this.loginCheck('进阶功能必须登录后才可以使用，是否登录')
    if (!sign) {
      return sign
    }
    
    // 登录后检查APP购买情况
    if (!this.hasBaguaApp()) {
      msg.modal({
        title: '需要解锁',
        content: '该功能需要解锁进阶功能后才可使用，是否前往购买'
      })
      .then((res) => {
        if (res) {
          router.push('buy')
        }
      })
      return false
    }
    
    return sign
  }
  
  hasBaguaApp () {
    // 当前用户是否具有APP
    if (this.baguaAppData.expiredTime === 'forever') {
      // 永久使用权限
      return true
    }
    const nowDateStamp = new Date().getTime()
    if (this.baguaAppData.expiredTime >= nowDateStamp) {
      // 还在使用期限内
      return true
    }
    return false
  }
  
  // 调用用户相关云函数，需要验证token，对于token过期有特殊提示
  // isRetry 本次请求是不是重试请求，如果为true代表当前请求为重试请求，无需再次重试
  async userCloudAuth (name = 'user-center', action, data = {}, isRetry = false) {
    try {
      console.log('云函数', name, action, data)
      const res = await req.cloud(name, {
        action: action,
        params: data
      })
      return res
    }
    catch (e) {
      if (typeof e.detail === 'object' && e.detail.code === 30203) {
        // 30203 token过期，需要提示登录信息失效，引导用户再次登录
        
        // #ifdef H5
        // H5提示用户登录信息过期：您的登录信息已过期，是否重新登录
        const isConfirm = await msg.modal({
          title: '登录失效',
          content: '当前登录信息已过期，是否重新登录'
        })
        
        if (isConfirm) {
          // H5手动登录
          // 微信H5自动登录
        }
        // #endif
        
        // #ifdef MP
        if (!isRetry) {
          // 重新登录后再进行一次尝试
          const loginRes = await this.mpLogin()
          if (action === 'checkToken') {
            // token检查本就是获取用户数据，登录后无需重试了
            return loginRes
          }
          else {
            return await this.userCloudAuth(name, action, data, true)
          }
        }
        // #endif
      }
      
      // 其他错误都代表自动登录失败，返回错误
      return Promise.reject(e)
    }
  }
  
  // 静态，调用用户相关云函数，不会处理token过期的错误，一律视为reject
  static async userCloud (name = 'user-center', action, data = {}) {
    console.log('云函数', name, action, data)
    const res = await req.cloud(name, {
      action: action,
      params: data
    })
    return res
  }
  
  // 获取登录凭证code，provider为服务提供商
  // 可用于小程序与APP
  static getCode (provider = null) {
    return new Promise((resolve, reject) => {
      // 先构建登录信息对象
      let data = {}
      if (provider !== null) {
        data.provider = provider
      }
      
      uni.login({
        ...data,
        success: (res) => {
          resolve(res.code)
        },
        fail: (e) => {
          reject(err('LOGIN_CODE_ERROR', e))
        }
      })
    })
  }
  
  // 获取用户信息，provider为服务提供商
  static getUserInfo (provider = null) {
    return new Promise((resolve, reject) => {
      // 先构建登录信息对象
      let data = {}
      if (provider !== null) {
        data.provider = provider
      }
      
      uni.getUserInfo({
        ...data,
        success: (res) => {
          resolve(res)
        },
        fail: (e) => {
          reject(err('GET_INFO_ERROR', e))
        }
      })
    })
  }
  
  static pay (data = {}) {
    return new Promise((resolve, reject) => {
      const provider = User.platformOptions.jsApiProvider
      
      uni.requestPayment({
        provider: provider,
        ...data,
        success: (res) => {
          console.log(res)
          resolve(res)
        },
        fail: (e) => {
          reject(err('PAY_ERROR', e))
        }
      })      
    })
  }
  
  // 保存自动登录信息
  static setToken (token, expired) {
    // 存储uniIdToken这个字段，uniCloud每次都会在调用云函数时自动获取
    // 2.7.14 版本token存储在storage内使用的是驼峰形式的键值uniIdToken，下版会调整为蛇形uni_id_token，调整后会在一段时间内兼容驼峰形式
    uni.setStorageSync('uniIdToken', token)
    uni.setStorageSync('uni_id_token_expired', expired)
  }
  
  // 删除自动登录信息
  static removeToken () {
    uni.removeStorageSync('uniIdToken')
    uni.removeStorageSync('uni_id_token_expired')
  }
}

export default User